#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <pthread.h>

#include "JackClient.h"
#include "UnitManager.h"

#define MIN(a,b) ((a < b) ? (a) : (b))
#define MAX(a,b) ((a > b) ? (a) : (b))

int onProcess(jack_nframes_t nframes, void *arg)
{
   JackClient *client = (JackClient*) arg;
   jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer(client->mOutputPort, nframes);

   /*
   pthread_mutex_lock(&client->mBufMutex);

   size_t fromBuf = MIN(client->mBufSize, nframes);
   memcpy(out, client->mBuffer, fromBuf * sizeof(sample_t));

   if (client->mBufSize < nframes)
      UnitManager::getInstance().getDac()->ar(0, &out[client->mBufSize], nframes - client->mBufSize, Options());

   client->mBufUtilized = MAX(fromBuf, nframes);

   pthread_mutex_unlock(&client->mBufMutex);
   */

   UnitManager::getInstance().getDac()->ar(0, out, nframes, Options());

   return 0;      
}

void onShutdown(void *arg)
{
   //JackClient *client = (JackClient*) arg;
   fprintf(stderr, "Jack shutdown");
   exit(1);
}

/* Private constructor for the singleton */
JackClient::JackClient() throw(TextException)
{
   mSampleRate = 44100;

   mBuffer = NULL;
   mBufSize = 0;
   mBufUtilized = 0;
   pthread_mutex_init(&mBufMutex, NULL);

   return; /*=A=*/

   jack_options_t options = JackNullOption;
   jack_status_t status;
   ((mClient = jack_client_open("TestJackClient", options, &status)) == 0)
      || textException("jack_client_open() failure");

   jack_set_process_callback(mClient, onProcess, (void*)this);
   jack_on_shutdown(mClient, onShutdown, (void*)this);
   mSampleRate = jack_get_sample_rate(mClient);
   mOutputPort = jack_port_register(mClient, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);

   (jack_activate(mClient) == 0) || textException("jack_activate() failure");
}

void JackClient::shutdown()
{
   jack_client_close(mClient);
   exit(0);
}

void JackClient::resizeBuffer(size_t newSize)
{
   if (mBuffer == NULL)
   {
      mBuffer = (sample_t*) malloc(newSize * sizeof(sample_t));
      if (mBuffer == NULL)
         textException("buffer allocation failure");

      mBufSize = newSize;
   }
   else
   {
      mBuffer = (sample_t*) realloc(mBuffer, newSize * sizeof(sample_t));
      if (mBuffer == NULL)
         textException("buffer allocation failure");
      mBufSize = newSize;
   }
}

void JackClient::bgProcess()
{
   pthread_mutex_lock(&mBufMutex);

   if (mBufUtilized > mBufSize)
   {
      resizeBuffer(mBufUtilized);
      mBufUtilized = 0;
   }
   else if (mBufUtilized < mBufSize)
   {
      memcpy(mBuffer, &mBuffer[mBufUtilized], (mBufSize - mBufUtilized) * sizeof(sample_t));
   }
   else
   {
      mBufUtilized = 0;
   }

   UnitManager::getInstance().getDac()->ar(0, &mBuffer[mBufUtilized], mBufSize - mBufUtilized, Options());

   pthread_mutex_unlock(&mBufMutex);
}

